﻿//  Copyright © Decebal Mihailescu 2013-2014
//  All rights reserved. This source code and binaries are distributed under 
//  Microsoft Public PatternLicense (Ms-PL)
//  The latest code is available at http://VSDesignPatterns.codeplex.com

//  Redistributions of source code must retain the above copyright notice, 
//  this list of conditions and the following disclaimer. 
//  Redistributions in binary form must reproduce the above copyright notice, 
//  this list of conditions and the following disclaimer in the documentation 
//  and/or other materials provided with the distribution. 

//  THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
//  KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
//  IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
//  PURPOSE. IT CAN BE DISTRIBUTED FREE OF CHARGE AS LONG AS THIS HEADER 
//  REMAINS UNCHANGED.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using System.IO;
using System.Globalization;
using System.Diagnostics;
using System.Windows.Forms;

namespace Common
{
#if !VS2015
    using Ionic.Zip;
#endif
    public class TemplateArgument : ITemplateArgument
    {
        ITemplatePattern _iParentTemplate;
        public TemplateArgument(ITemplatePattern iparentTemplate, XElement xmlArgument)
        {
            _iParentTemplate = iparentTemplate;
            if (xmlArgument == null || xmlArgument.Name != "TemplateArgument")
                return;
            if (xmlArgument.Attribute("ArgumentName") != null)
                ArgumentName = xmlArgument.Attribute("ArgumentName").Value;
            if (xmlArgument.Attribute("Description") != null)
                Description = xmlArgument.Attribute("Description").Value;
            Value = xmlArgument.Value;

        }
#region IServiceProvider Members

        public object GetService(Type serviceType)
        {
            if (serviceType == typeof(ITemplateArgument) || serviceType == typeof(IServiceProvider))
                return this;
            object res = null;
            res = (_iParentTemplate == null) ? null : _iParentTemplate.GetService(serviceType);
            return res;
        }

#endregion
#region ITemplateArgument Members
        public string ArgumentName { get; private set; }
        public string Description { get; private set; }
        public string Value { get; set; }
        public bool IsRuntime { get; private set; }
#endregion


    }
    public class TemplateRuntimeArgument : ITemplateArgument
    {
        ITemplatePattern _parentTemplate;
        IProjectInfo _projectInfo;
        public TemplateRuntimeArgument(ITemplatePattern parentTemplate, XElement xmlArgument)
        {
            _parentTemplate = parentTemplate;
            _projectInfo = _parentTemplate.GetAsService<IProjectInfo>();
            if (xmlArgument == null || xmlArgument.Name != "TemplateArgument")
                return;
            if (xmlArgument.Attribute("ArgumentName") != null)
                ArgumentName = xmlArgument.Attribute("ArgumentName").Value;
            if (xmlArgument.Attribute("Description") != null)
                Description = xmlArgument.Attribute("Description").Value;
            string val;
            if (_projectInfo.ProjectProperties.TryGetValue(ArgumentName, out val))
                Value = val;
        }
#region IServiceProvider Members

        public object GetService(Type serviceType)
        {
            if (serviceType == typeof(TemplateRuntimeArgument) || serviceType == typeof(IServiceProvider))
                return this;
            object res = null;
            if (serviceType == typeof(IProjectInfo))
                res = (_projectInfo == null) ? null : _projectInfo.GetService(serviceType);
            if (res != null)
                return res;
            res = (_parentTemplate == null) ? null : _parentTemplate.GetService(serviceType);
            return res;
        }

#endregion
#region ITemplateArgument Members
        public string ArgumentName { get; private set; }
        public string Description { get; private set; }
        public string Value { get; set; }
        public bool IsRuntime { get; private set; }
#endregion


    }

    public class TemplateFile : ITemplateFile
    {

        ITemplatePattern _iParentTemplate;

        FileData GeneratePatternFile(IProjectInfo projectInfo, TemplateFile templateFile)
        {
            return null;
        }
        IRepository _iRepository;
        public TemplateFile(ITemplatePattern iParentTemplate, XElement fileElement)
        {
            _iParentTemplate = iParentTemplate;
            _iRepository = _iParentTemplate.GetAsService<IRepository>();
            ILanguageProviderMetaData ilpm = _iParentTemplate.GetAsService<ILanguageProviderMetaData>();
            StoredAs = (ilpm == null) ? StorageType.FileSystem : ilpm.SavedAs;
            if (fileElement == null)
                return;
            switch (fileElement.Name.LocalName)
            {
                case "FileTemplate":
                    FileItemName = fileElement.Attribute("FileItemName").Value;
                    FileTemplate = fileElement.Value;
                    StoredAs = fileElement.Attr2Enum<StorageType>("StoredAs", StoredAs);
                    break;
                default: return;
            }
            _iRepository.LoadTFContent(this);
        }

        public string FileItemName { get; set; }
        public string FileTemplate { get; set; }
        public StorageType StoredAs { get; private set; }
        public string FileTemplateContent { get; set; }
#region IServiceProvider Members

        public object GetService(Type serviceType)
        {
            if (serviceType == typeof(ITemplateFile) || serviceType == typeof(IServiceProvider))
                return this;
            object res = null;
            res = (_iParentTemplate == null) ? null : _iParentTemplate.GetService(serviceType);
            return res;
        }



#endregion
    }

    public class TemplatePattern : IServiceProvider, ITemplatePattern
    {
        readonly static string[] _countryCodes;
        public string ConvertThreeLetterNameToTwoLetterName(string name)
        {
            if (name.Length != 3)
            {
                throw new ArgumentException("name must be three letters.");
            }

            name = name.ToUpper();

            CultureInfo[] cultures = CultureInfo.GetCultures(CultureTypes.SpecificCultures);
            foreach (CultureInfo culture in cultures)
            {
                RegionInfo region = new RegionInfo(culture.LCID);
                if (region.ThreeLetterISORegionName.ToUpper() == name)
                {
                    return region.TwoLetterISORegionName;
                }
            }

            return null;
        }
        static readonly float _fCtrVSVersion;
        static TemplatePattern()
        {
            try
            {
                _fCtrVSVersion = float.Parse(Utils.VSVersion, CultureInfo.InvariantCulture.NumberFormat);
            }
            catch
            {

                _fCtrVSVersion = 9.0F;
            }
            //see http://stackoverflow.com/questions/4884692/converting-country-codes-in-net
            CultureInfo[] cultures = CultureInfo.GetCultures(CultureTypes.SpecificCultures);
            _countryCodes = cultures.Select(culture =>
                {
                    try
                    {
                        var region = new RegionInfo(culture.LCID);
                        return region.ThreeLetterISORegionName;
                    }
                    catch (Exception ex)
                    {
                        Logging.Instance.Log(2, "static TemplatePattern: Can't find 3 letter culture for {0}, exception:{1}", culture.EnglishName, ex.Message);
                        return null;
                    }
                }).Where(s => s != null).ToArray();

        }
        protected ILanguageProvider _languageProvider;
        static readonly string[] _descrExts = new string[] { ".htm", ".html", ".rtf", ".txt" };
        static readonly string[] _pictExts = new string[] { ".png", ".jpg", ".gif", ".bmp" };
        StorageType DefaultStorage = StorageType.FileSystem;
        IRepository _iRepository;
        public TemplatePattern() { }
        public virtual void UIFormatTemplatePattern(DataGridViewCellFormattingEventArgs e)
        {//just a placeholder
        }
        public virtual bool CanGeneratePattern(IWizardWindow iww)
        {
            return true;
        }
        public virtual int TemplateVersion { get { return 0; } }
        public virtual void Init(ILanguageProvider languageProvider, XElement templateElem)
        {
            _languageProvider = languageProvider;

            if (_languageProvider != null)
            {
                //get defaultStorage from languageProvider
                ILanguageProviderMetaData ilpm = _languageProvider.GetAsService<ILanguageProviderMetaData>();
                DefaultStorage = (ilpm == null) ? StorageType.FileSystem : ilpm.SavedAs;
                _iRepository = _languageProvider.GetAsService<IRepository>();
            }
            if (templateElem == null || templateElem.Attribute("PatternName") == null
                || templateElem.Attribute("CodeLang") == null || templateElem.Name != "PatternTemplate")
            {
                return;//bad xml
            }
            PatternName = templateElem.Attribute("PatternName").Value;
            try
            {
                try
                {
                    this.CodeLanguage = (CodeLangEnum)Enum.Parse(typeof(CodeLangEnum), templateElem.Attribute("CodeLang").Value, true);
                }
                catch
                {

                    CodeLanguage = CodeLangEnum.None;
                }

                var properties = templateElem.Element("PatternProperties");
                PictureStoredAs = DefaultStorage;
                DescriptionStoredAs = DefaultStorage;
                Culture = "eng";
                MinVSVersion = _fCtrVSVersion;
                if (properties != null)
                {
                    float minVSVer = _fCtrVSVersion;
                    foreach (XElement e in properties.Elements())
                    {
                        switch (e.Name.ToString())//.ToLowerInvariant()
                        {
                            case "PatternAlias":
                                PatternAlias = e.Value;
                                break;
                            case "MinVSVersion":
                                if (!float.TryParse(e.Value, out minVSVer))
                                    minVSVer = _fCtrVSVersion;
                                MinVSVersion = minVSVer;
                                break;
                            case "Picture":
                                _iRepository.GetPicture(this, e);

                                break;

                            case "OnlineResources":
                                OnlineResources = e.Value;
                                break;
                            case "CompilerSyntax":
                                CompilerSyntax = e.Value;
                                break;

                            case "ProgrammingStyle":
                                ProgrammingStyle = e.Value;
                                break;
                            case "Description":
                                _iRepository.GetDescription(this, e);
                                break;

                            case "Category":
                                Category = e.Value;
                                break;
                            case "Culture":
                                Culture = e.Value;//"eng" if missing
                                break;

                            default:
                                break;



                        }
                    }
                }
                //fill in with defaults
                IProjectInfo projectInfo = _languageProvider.GetAsService<IProjectInfo>();
                if (OnlineResources == null)
                {

                    OnlineResources = string.Format(@"www.bing.com/search?q={0}+software+design+pattern+{1}", PatternName, projectInfo.CodeLang);
                }
                if (Description == null)
                {//try adding default file if value not in xml file

                    _iRepository.GetDescription(this, null);

                }
                if (Picture == null)
                {//try adding default file if value not in xml file
                    _iRepository.GetPicture(this, null);

                }


                if (ProgrammingStyle == null)
                {

                    ProgrammingStyle = "Plain";
                }
                if (PatternAlias == null)
                {

                    PatternAlias = PatternName;
                }
                LoadCodeTemplates(templateElem);
            }
            catch (Exception ex)
            {
                IWin32Window iwnd = _languageProvider.GetAsService<IWizardWindow>() as IWin32Window;
                iwnd.ShowMessageBox(ex.Message, "Can not initialize template " + PatternName);
            }

        }

        protected virtual void LoadCodeTemplates(XElement templateElem)
        {
            //add default File template xml element if missing
            var xmlFileArgs = templateElem.Element("FileTemplates");
            if (xmlFileArgs == null)
            {
                xmlFileArgs = new XElement("FileTemplates", GetDefaultFileTemplate());
                templateElem.Add(xmlFileArgs);
            }
            else
            {

                if (!xmlFileArgs.Elements().Any())
                {
                    XElement defaultElem = GetDefaultFileTemplate();
                    xmlFileArgs.Add(defaultElem);
                }
            }
            TemplateFiles = xmlFileArgs.Elements("FileTemplate").Select(e => new TemplateFile(this, e) as ITemplateFile);//.ToArray();

            var xmlargs = templateElem.Element("TemplateArguments");
            if (xmlargs == null)
            {
                xmlargs = new XElement("TemplateArgument", GetDefaultArgTemplate());
                templateElem.Add(xmlargs);
            }
            else
            {

                if (!xmlargs.Elements().Any())
                {
                    XElement defaultElem = this.GetDefaultArgTemplate();
                    xmlargs.Add(defaultElem);
                }
            }

            Arguments = xmlargs.Elements("TemplateArgument").Select(e =>
            {
                if (e.Attribute("IsRuntime") == null || e.Attribute("IsRuntime").Value.ToUpperInvariant() == "FALSE")
                {
                    return new TemplateArgument(this, e) as ITemplateArgument;
                }
                else
                {
                    return new TemplateRuntimeArgument(this, e) as ITemplateArgument;
                }
            });
        }

        private XElement GetDefaultFileTemplate()
        {
            string fileItemName = null;
            string fileTemplate = null;
            switch (this.CodeLanguage)
            {
                case CodeLangEnum.CSharp:
                    fileTemplate = PatternName + "Template.cs";
                    fileItemName = PatternName + ".cs";
                    break;
                case CodeLangEnum.VB:
                    fileTemplate = PatternName + "Template.vb";
                    fileItemName = PatternName + ".vb";
                    break;
                case CodeLangEnum.ManagedCpp:
                case CodeLangEnum.NativeCpp:
                    fileTemplate = PatternName + "Template.h";// no .cpp for this template
                    fileItemName = PatternName + ".h";
                    break;
                default: return null;
            }
            XElement defaultElem = new XElement("FileTemplate", fileTemplate, new XAttribute("FileItemName", fileItemName)
                , new XAttribute("StoredAs", DefaultStorage.ToString())
                );
            return defaultElem;
        }

        private XElement GetDefaultArgTemplate()
        {
            XElement defaultElem = new XElement("TemplateArgument", new XAttribute("ArgumentName", "RootNamespace")
                , new XAttribute("IsRuntime", "True"), new XAttribute("Description", "Name Space")
                );
            return defaultElem;
        }

        public virtual IEnumerable<ITemplateArgument> Arguments { get; set; }
        public float MinVSVersion { get; set; }// will filter features like optional params, async/await,dynamic
        public string Picture { get; set; }
        public StorageType PictureStoredAs { get; set; }
        public string OnlineResources { get; set; }
        public string CompilerSyntax { get; set; }//managed,native,VC10, empty
        public string ProgrammingStyle { get; set; }//generic,template,plain,nothing
        public string PatternName { get; set; }
        public string PatternAlias { get; set; }
        public string Description { get; set; }
        public StorageType DescriptionStoredAs { get; set; }
        public CodeLangEnum CodeLanguage { get; set; }//string version of CodeLanguageEnum
        public string Category { get; set; }//behavioral,creational
        public string Culture { get; set; }
        // public virtual bool IsDefault { get { return true; } }//TO DO get the val from  language provider
        public virtual IEnumerable<ITemplateFile> TemplateFiles { get; set; }
#region IServiceProvider Members

        public object GetService(Type serviceType)
        {
            if (serviceType == typeof(ITemplatePattern) || serviceType == typeof(IServiceProvider))
                return this;
            if (serviceType == typeof(ILanguageProvider))
                return _languageProvider;
            object res = null;
            res = (_languageProvider == null) ? null : _languageProvider.GetService(serviceType);
            return res;
        }

#endregion
    }
}
